import type { Method, AxiosRequestConfig, ResponseType } from 'axios'
import { Md5 as md5 } from 'ts-md5/dist/md5'
import { get } from 'lodash-es'
import axios from './request'

const timeGroup = Object.create(null)

interface Url {
    url: string
    baseURL: string
}

interface Option extends AxiosRequestConfig {
    isCache: boolean
    delayed: number
    isDebounce: boolean
    debounceDelayed: number
}
type Param = {
    [key: string]: any
}

class Resource {
    private readonly _baseURL: string
    private readonly _url: string
    private options: Option
    private param: string
    private key: string
    private now: number
    constructor({ url, baseURL }: Url) {
        this._baseURL = baseURL
        this._url = url

        this.param = '?'
        this.options = Object.create(null)
        this.key = ''
        this.now = 0
    }

    stream(method = 'get', params: Param = {}, option: Param = {}) {
        this.options = Object.assign(
            {
                isCache: false, // 是否缓存数据
                delayed: 60, // 缓存数据时长(秒)
                isDebounce: false, // 是否接口防抖
                debounceDelayed: 800, // 接口防抖时长(毫秒)
                headers: {
                    'Content-Type': 'application/json',
                    // 'Content-Type': 'multipart/form-data',
                    // 'Content-Type': 'application/x-www-form-urlencoded',
                },
				responseType: 'json' as ResponseType,
            },
            option
        )
        const config: AxiosRequestConfig = {
            baseURL: this._baseURL,
            url: this._url,
            method: method as Method,
            headers: this.options.headers,
            responseType: this.options.responseType,
        }
        method.toUpperCase() === 'GET'
            ? (config['params'] = params)
            : (config['data'] = params)
        this.now = new Date().getTime() / 1000
        for (const i in params) {
            this.param += `${i}=${params[i]}&`
        }
        const key = JSON.stringify(
            this._baseURL + this._url + this.param.replace(/[&?]$/, '')
        )
        this.key = md5.hashStr(key).toString()
        // 判断是否缓存
        if (this.options.isCache) {
            const Cache = JSON.parse(<string>localStorage.getItem(this.key))
            if (Cache) {
                // 判断是否超时以进行缓存
                const timeout = Cache.time + this.options.delayed < this.now
                if (!timeout) {
                    return Cache.cacheData
                }
            }
        }

        // 判断是否防抖
        return new Promise(async (resolve, reject) => {
            if (this.options.isDebounce) {
                const md5key = md5.hashStr(this.key).toString()
                clearTimeout(timeGroup[md5key])
                timeGroup[md5key] = setTimeout(async () => {
                    this.createRequest(config).then((data) =>
                        resolve({
                            ...get(data, 'data.data', {}),
                        })
                    )
                }, this.options.debounceDelayed)
            } else {
                this.createRequest(config).then((data) =>
                    resolve({
                        ...get(data, 'data.data', {}),
                    })
                )
            }
        })
    }

    private async createRequest(config: AxiosRequestConfig) {
        const asyncData = await axios.request(config)
        // 缓存数据
        if (this.options.isCache) {
            localStorage.setItem(
                this.key,
                JSON.stringify({
                    time: this.now,
                    cacheData: asyncData,
                })
            )
        }
        return asyncData
    }
}

// 副接口拼接
const stream = (URL: string, baseURL: string) => (url: string = '') => {
    const path: string = url ? URL + url : URL
    return new Resource({ url: path, baseURL: baseURL })
}

export default stream
